/**************************************************************************************
*
*	Copyright (C) 2009 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	-	RenderPass.cpp
*
*	Comments	-	See RenderPass.h
*
**************************************************************************************/
#include "../Include/RenderPass.h"
#include "../Include/InputLayout.h"
#include "../Include/GraphicsResourceManager.h"
#include "../Include/BlendState.h"
#include "../Include/DepthStencilState.h"
#include "../Include/RasterizerState.h"
#include "../Include/GraphicsPipeline.h"

namespace Pulse
{
	RenderPass::RenderPass( void )
		: m_renderViewStage( ERenderViewStage::UNKNOWN ), 
		m_pDepthStencilState(PSX_NULL), m_pBlendState(PSX_NULL), m_pRasterizerState(PSX_NULL)
	{
		ClearShaders( FALSE );
	}

	RenderPass::RenderPass( VertexShader *pVS, PixelShader *pPS )
	{
		ClearShaders( FALSE );
	}

	RenderPass::~RenderPass( void )
	{
		ClearShaders( TRUE );
		PSX_SafeRelease( m_pBlendState );
		PSX_SafeRelease( m_pDepthStencilState );
		PSX_SafeRelease( m_pRasterizerState );
	}

	void RenderPass::SetVertexShader( VertexShader *pShader )
	{ 
		PSX_SafeRelease(m_pVertexShader);

		m_pVertexShader = pShader;

		if ( pShader )
			pShader->AddRef();
	}

	void RenderPass::SetVertexShader( Shader *pShader )
	{
		if ( pShader->GetType() == EShader::VERTEX )
			SetVertexShader( (VertexShader*)pShader );
	}

	void RenderPass::SetHullShader( HullShader *pShader ) 
	{ 
		PSX_SafeRelease(m_pHullShader);

		m_pHullShader = pShader; 

		if ( pShader )
			pShader->AddRef();
	}

	void RenderPass::SetHullShader( Shader *pShader )
	{
		if ( pShader->GetType() == EShader::HULL )
			SetVertexShader( (HullShader*)pShader );
	}

	void RenderPass::SetDomainShader( DomainShader *pShader ) 
	{ 
		PSX_SafeRelease(m_pDomainShader);

		m_pDomainShader = pShader; 

		if ( pShader )
			pShader->AddRef();
	}

	void RenderPass::SetDomainShader( Shader *pShader )
	{
		if ( pShader->GetType() == EShader::DOMAIN_ )
			SetVertexShader( (DomainShader*)pShader );
	}

	void RenderPass::SetGeometryShader( GeometryShader *pShader ) 
	{ 
		PSX_SafeRelease(m_pGeometryShader);

		m_pGeometryShader = pShader; 

		if ( pShader )
			pShader->AddRef();
	}

	void RenderPass::SetGeometryShader( Shader *pShader )
	{
		if ( pShader->GetType() == EShader::GEOMETRY )
			SetVertexShader( (GeometryShader*)pShader );
	}

	void RenderPass::SetPixelShader( PixelShader *pShader ) 
	{ 
		PSX_SafeRelease(m_pPixelShader);

		m_pPixelShader = pShader; 

		if ( pShader )
			pShader->AddRef();
	}

	void RenderPass::SetPixelShader( Shader *pShader )
	{
		if ( pShader->GetType() == EShader::PIXEL )
			SetVertexShader( (PixelShader*)pShader );
	}

	void RenderPass::SetComputeShader( ComputeShader *pShader ) 
	{ 
		PSX_SafeRelease(m_pComputeShader);

		m_pComputeShader = pShader; 

		if ( pShader )
			pShader->AddRef();
	}

	void RenderPass::SetComputeShader( Shader *pShader )
	{
		if ( pShader->GetType() == EShader::COMPUTE )
			SetComputeShader( (ComputeShader*)pShader );
	}

	Shader * RenderPass::GetShader( EShader::Type type )
	{
		Shader *pRet = m_pShaders[type];
		if ( pRet )
		{
			pRet->AddRef();
			return pRet;
		}

		return PSX_NULL;
	}

	void RenderPass::SetBlendState( BlendState *pState )
	{
		PSX_SafeRelease( m_pBlendState );

		m_pBlendState = pState;

		if ( pState )
			pState->AddRef();
	}

	void RenderPass::SetDepthStencilState( DepthStencilState *pState )
	{
		PSX_SafeRelease( m_pDepthStencilState );

		m_pDepthStencilState = pState;

		if ( pState )
			pState->AddRef();
	}

	void RenderPass::SetRasterizerState( RasterizerState *pState )
	{
		PSX_SafeRelease( m_pRasterizerState );

		m_pRasterizerState = pState;

		if ( pState )
		{
			pState->AddRef();
		}
	}

	GeometryElementRequirement * RenderPass::GetGeometryRequirements( void )
	{
		if ( m_geometryRequirements.GetSize() )
			return &m_geometryRequirements[0];

		return PSX_NULL;
	}

	MaterialElementRequirement * RenderPass::GetMaterialRequirements( void )
	{
		if ( m_materialRequirements.GetSize() )
			return &m_materialRequirements[0];

		return PSX_NULL;
	}

	void RenderPass::Update( void )
	{
		for ( INT i = 0; i < EShader::EnumCount; ++i )
		{
			if ( m_pShaders[i] )
			{
				//m_pShaders[i]->Update(;
			}
		}
	}

	void RenderPass::ConfigurePipeline( GraphicsPipeline *pPipeline )
	{
		// Bind states
		//////////////
		if ( m_pDepthStencilState )
		{
			pPipeline->OMSetDepthStencilState( m_pDepthStencilState );
			pPipeline->GetOutputMergerStage()->BindDepthStencilState();
		}

		if ( m_pBlendState )
		{
			pPipeline->OMSetBlendState( m_pBlendState );
			pPipeline->GetOutputMergerStage()->BindBlendState();
		}
		
		if ( m_pRasterizerState )
		{
			pPipeline->RSSetState( m_pRasterizerState );
			pPipeline->GetRasterizerStage()->BindRasterizerState();
		}

		// Bind shaders
		///////////////

		pPipeline->VSSetShader( m_pVertexShader );
		pPipeline->PSSetShader( m_pPixelShader );
		pPipeline->CSSetShader( m_pComputeShader );

		pPipeline->GetVertexShaderStage()->BindShader();
		pPipeline->GetPixelShaderStage()->BindShader();
		pPipeline->GetComputeShaderStage()->BindShader();

		// Bind render states
		///////////////////////

		pPipeline->OMSetDepthStencilState( m_pDepthStencilState );
		pPipeline->OMSetBlendState( m_pBlendState );
		pPipeline->RSSetState( m_pRasterizerState );

		pPipeline->GetOutputMergerStage()->BindDepthStencilState();
		pPipeline->GetOutputMergerStage()->BindBlendState();
		pPipeline->GetRasterizerStage()->BindRasterizerState();

		// Update render parameters
		////////////////////////////

		if ( m_pVertexShader )
		{
			m_pVertexShader->Update( pPipeline );
		}
		
		if ( m_pPixelShader )
		{
			m_pPixelShader->Update( pPipeline );
		}

		if ( m_pComputeShader )
		{
			m_pComputeShader->Update( pPipeline );
		}

		//Update();
	}

	void RenderPass::ClearShaders( bool bCheckRelease )
	{
		if ( bCheckRelease )
		{
			for ( INT i = 0; i < EShader::EnumCount; ++i )
				if ( m_pShaders[i] )
					m_pShaders[i]->Release();
		}

		PSX_ZeroMem( m_pShaders, sizeof(m_pShaders) );
	}

	void RenderPass::AddGeometryRequirement( GeometryElementRequirement *pRequirement )
	{
		if ( pRequirement )
			m_geometryRequirements.PushBack( *pRequirement );
		else
			PSX_PushWarning( "Invalid geometry requirement." );
	}

	void RenderPass::AddMaterialRequirement( MaterialElementRequirement *pRequirement )
	{
		if ( pRequirement )
			m_materialRequirements.PushBack( *pRequirement );
		else
			PSX_PushWarning( "Invalid material requirement." );
	}

}
